/***************************************************************************
 *                                                                         *
 *                             HashFunction.java                           *
 *                            -------------------                          *
 *   date                 : 16.08.2004                                     *
 *   copyright            : (C) 2004-2008 Distributed and                  *
 *                              Mobile Systems Group                       *
 *                              Lehrstuhl fuer Praktische Informatik       *
 *                              Universitaet Bamberg                       *
 *                              http://www.uni-bamberg.de/pi/              *
 *   email                : sven.kaffille@uni-bamberg.de                   *
 *   			    		karsten.loesing@uni-bamberg.de                 *
 *                                                                         *
 *                                                                         *
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   A copy of the license can be found in the license.txt file supplied   *
 *   with this software or at: http://www.gnu.org/copyleft/gpl.html        *
 *                                                                         *
 ***************************************************************************/
package de.uniba.wiai.lspi.chord.service.impl;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import de.uniba.wiai.lspi.chord.data.ID;
import de.uniba.wiai.lspi.chord.data.URL;
import de.uniba.wiai.lspi.chord.service.Key;

/**
 * Provides a hash function for calculating the hash value out of {@link Key} or
 * {@link URL} objects.
 * 
 * @author Karsten Loesing, Sven Kaffille
 * @version 1.0.5
 */
final class HashFunction {

	/**
	 * Singleton instance of this class.
	 */
	private static HashFunction hashFunction = null;

	/**
	 * Returns the singleton instance of this class.
	 * 
	 * @return Singleton instance of HashFunction.
	 * @throws RuntimeException
	 *             If there is an internal problem with the specified hash
	 *             function of the Java API.
	 */
	static HashFunction getHashFunction() {

		if (hashFunction == null) {
			try {
				hashFunction = new HashFunction(MessageDigest
						.getInstance("SHA-1"));
			} catch (NoSuchAlgorithmException e) {
				throw new RuntimeException("No hash function available!", e);
			}
		}

		return hashFunction;

	}

	/**
	 * Message digest for calculating hash values.
	 */
	private MessageDigest messageDigest;

	/**
	 * Constructor is hidden and only invoked once by {@link #getHashFunction()}.
	 * 
	 * @param digest1
	 *            Message digest for calculating hash values.
	 */
	private HashFunction(MessageDigest digest1) {
		if (digest1 == null) {
			throw new NullPointerException("Parameter may not be null!");
		}
		this.messageDigest = digest1;
	}

	/**
	 * Returns the length of IDs generated by this hash function.
	 * 
	 * @return Number of bytes of generated IDs.
	 */
	final int getLengthOfIDsInBytes() {
		return this.messageDigest.getDigestLength();
	}

	/**
	 * Calculates the hash value for a given data Key.
	 * 
	 * @param entry
	 * @return ID for the given Key.
	 */
	final ID getHashKey(Key entry) {

		if (entry == null) {
			throw new IllegalArgumentException(
					"Parameter entry must not be null!");
		}
		if (entry.getBytes() == null || entry.getBytes().length == 0) {
			throw new IllegalArgumentException(
					"Byte representation of Parameter must not be null or have length 0!");
		}

		byte[] testBytes = entry.getBytes();
		return this.createID(testBytes);
	}

	private final ID createID(byte[] testBytes) {
		/*
		 * synchronize access to messageDigest, so that messageDigest cannot be
		 * reset by another thread before calculating digest.
		 */
		synchronized (this.messageDigest) {
			this.messageDigest.reset();
			this.messageDigest.update(testBytes);
			return new ID(this.messageDigest.digest());
		}
	}

	/**
	 * Calculates the hash value for a given URL.
	 * 
	 * @param incomingURL
	 *            URL of current node which is used to create a pseudo-unique
	 *            ID.
	 * @return ID for the given URL.
	 * @throws IllegalArgumentException
	 *             If <code>incomingURL</code> is null!
	 */
	final ID createUniqueNodeID(URL incomingURL) {
		if (incomingURL == null) {
			throw new IllegalArgumentException("URL must not be null!");
		}
		String id = incomingURL.toString();
		ID resultKey = this.createID(id.getBytes());
		return resultKey;
	}

}